package com.black.utils.compare;

import lombok.Getter;
import net.sourceforge.pinyin4j.PinyinHelper;

import java.util.Comparator;

/**
 * @author ylx
 */
public class StringComparator implements Comparator<String>
{
    @Override
    public int compare(String s1, String s2)
    {
        if (s1 == null || s2 == null)
        {
            return compareNull(s1, s2);
        }
        // 逐个字符比较
        int minLength = Math.min(s1.length(), s2.length());
        for (int i = 0; i < minLength; i++)
        {
            // 获取字符的优先级
            char c1 = s1.charAt(i);
            char c2 = s2.charAt(i);

            Priority priority1 = getCharacterPriority(c1);
            Priority priority2 = getCharacterPriority(c2);

            // 优先根据优先级比较
            if (priority1 != priority2)
            {
                return priority1.value - priority2.value;
            }
            int res;
            switch (priority1)
            {
                case LETTER:
                    // 字母比较
                    res = compareLetter(c1, c2);
                    if (res != 0)
                        return res;
                    break;
                case CHINESE:
                    // 汉字首字母比较
                    res = compareChinese(c1, c2);
                    if (res != 0)
                        return res;
                    break;
                case NUMBER:
                case OTHER:
                    // 原String的compareTo 根据字符比较
                    if (c1 != c2)
                        return c1 - c2;
            }
        }

        // 长度比较
        return s1.length() - s2.length();
    }

    /**
     * null 排最后
     */
    private int compareNull(String s1, String s2)
    {
        if (s1 == null && s2 == null)
            // 都为 null 认为相等
            return 0;
        else
            // 为 null 的一方认为较大, 顺序后排
            return s1 == null ? 1 : -1;
    }

    /**
     * 获取字符的优先级
     *
     * @param c 字符
     * @return 优先级
     */
    private Priority getCharacterPriority(char c)
    {
        if (c >= '0' && c <= '9')
            // 数字最高优先级
            return Priority.NUMBER;
        else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            // 字母中 若需要兼容其他语言字母需要使用Character.isLetter(c)判断
            return Priority.LETTER;
        else if (isChinese(c))
            // 中文
            return Priority.CHINESE;
        // 其他最低优先级
        return Priority.OTHER;
    }

    private int compareLetter(char c1, char c2)
    {
        if (c1 == c2)
        {
            return 0;
        }
        // 忽略大小写比较
        int offset = Character.toUpperCase(c1) - Character.toUpperCase(c2);
        if (offset == 0)
        {
            // 相同字母小写优先
            return Character.isLowerCase(c1) ? -1 : 1;
        }
        return offset;
    }

    private int compareChinese(char c1, char c2)
    {
        char firstLetter1 = getFirstLetter(c1);
        char firstLetter2 = getFirstLetter(c2);
        // 先按拼音首字母比较
        int result = compareLetter(firstLetter1, firstLetter2);
        if (result != 0)
        {
            return result;
        }
        // 如果拼音首字母相同，则按Unicode码点比较
        return c1 - c2;
    }

    /**
     * 获取汉字的首字母（非汉字返回原字符）
     */
    private char getFirstLetter(char c)
    {
        String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
        if (pinyinArray == null || pinyinArray.length == 0)
            return c;
        String firstChar = pinyinArray[0];
        if (firstChar == null || firstChar.isEmpty())
            return c;
        return firstChar.charAt(0);
    }

    private boolean isChinese(char c)
    {
        return (c >= '\u4E00' && c <= '\u9FFF') ||  // 基本汉字
            (c >= '\u3400' && c <= '\u4DBF') ||  // 扩展A区
            (c >= '\uF900' && c <= '\uFAFF');    // 兼容汉字
    }

    @Getter
    private enum Priority
    {
        NUMBER(1),
        LETTER(2),
        CHINESE(3),
        OTHER(4);

        private final int value;

        Priority(int value)
        {
            this.value = value;
        }
    }
}
